using System;
using System.Diagnostics;
using Community.CsharpSqlite.Entity;

namespace Community.CsharpSqlite
{
    using sqlite3_stmt = Vdbe;

    public partial class Sqlite3
    {
        /*
    ** 2005 July 8
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** This file contains code associated with the ANALYZE command.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-03-09 19:31:43 4ae453ea7be69018d8c16eb8dabe05617397dc4d
    **
    **  $Header: Community.CsharpSqlite/src/analyze_c.cs,v 6604176a7dbe 2010/03/12 23:35:36 Noah $
    *************************************************************************
    */
#if !SQLITE_OMIT_ANALYZE
        //#include "sqliteInt.h"

        /*
    ** This routine generates code that opens the sqlite_stat1 table for
    ** writing with cursor iStatCur. If the library was built with the
    ** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is
    ** opened for writing using cursor (iStatCur+1)
    **
    ** If the sqlite_stat1 tables does not previously exist, it is created.
    ** Similarly, if the sqlite_stat2 table does not exist and the library
    ** is compiled with SQLITE_ENABLE_STAT2 defined, it is created. 
    **
    ** Argument zWhere may be a pointer to a buffer containing a table name,
    ** or it may be a NULL pointer. If it is not NULL, then all entries in
    ** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated
    ** with the named table are deleted. If zWhere==0, then code is generated
    ** to delete all stat table entries.
    */

        public struct _aTable
        {
            public string zCols;
            public string zName;

            public _aTable(string zName, string zCols)
            {
                this.zName = zName;
                this.zCols = zCols;
            }
        };

        private static void openStatTable(
            Parse pParse, /* Parsing context */
            int iDb, /* The database we are looking in */
            int iStatCur, /* Open the sqlite_stat1 table on this cursor */
            string zWhere /* Delete entries associated with this table */
            )
        {
            var aTable = new[]
                             {
                                 new _aTable("sqlite_stat1", "tbl,idx,stat"),
#if SQLITE_ENABLE_STAT2
new _aTable( "sqlite_stat2", "tbl,idx,sampleno,sample" ),
#endif
                             };

            var aRoot = new[] {0, 0};
            var aCreateTbl = new byte[] {0, 0};

            int i;
            sqlite3 db = pParse.db;
            Db pDb;
            Vdbe v = SelectHelper.GetVdbe(pParse);

            if (v == null) return;
            Debug.Assert(sqlite3BtreeHoldsAllMutexes(db));
            Debug.Assert(VdbeAux.VdbeDb(v) == db);
            pDb = db.aDb[iDb];

            for (i = 0; i < Utility.ArraySize(aTable); i++)
            {
                string zTab = aTable[i].zName;
                Table pStat;
                if ((pStat = Build.FindTable(db, zTab, pDb.zName)) == null)
                {
                    /* The sqlite_stat[12] table does not exist. Create it. Note that a 
          ** side-effect of the CREATE TABLE statement is to leave the rootpage 
          ** of the new table in register pParse.regRoot. This is important 
          ** because the OpenWrite opcode below will be needing it. */
                    Build.NestedParse(pParse,
                                       "CREATE TABLE %Q.%s(%s)", pDb.zName, zTab, aTable[i].zCols
                        );
                    aRoot[i] = pParse.regRoot;
                    aCreateTbl[i] = 1;
                }
                else
                {
                    /* The table already exists. If zWhere is not NULL, delete all entries 
          ** associated with the table zWhere. If zWhere is NULL, delete the
          ** entire contents of the table. */
                    aRoot[i] = pStat.tnum;
                    sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
                    if (!String.IsNullOrEmpty(zWhere))
                    {
                        Build.NestedParse(pParse,
                                           "DELETE FROM %Q.%s WHERE tbl=%Q", pDb.zName, zTab, zWhere
                            );
                    }
                    else
                    {
                        /* The sqlite_stat[12] table already exists.  Delete all rows. */
                        VdbeAux.VdbeAddOp2(v, OPCode.OP_Clear, aRoot[i], iDb);
                    }
                }
            }

            /* Open the sqlite_stat[12] tables for writing. */
            for (i = 0; i < Utility.ArraySize(aTable); i++)
            {
                VdbeAux.VdbeAddOp3(v, OPCode.OP_OpenWrite, iStatCur + i, aRoot[i], iDb);
                VdbeAux.VdbeChangeP4(v, -1, 3, P4Type.P4_INT32);
                VdbeAux.VdbeChangeP5(v, aCreateTbl[i]);
            }
        }

        /*
    ** Generate code to do an analysis of all indices associated with
    ** a single table.
    */

        private static void analyzeOneTable(
            Parse pParse, /* Parser context */
            Table pTab, /* Table whose indices are to be analyzed */
            int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */
            int iMem /* Available memory locations begin here */
            )
        {
            sqlite3 db = pParse.db; /* Database handle */
            Index pIdx; /* An index to being analyzed */
            int iIdxCur; /* Cursor open on index being analyzed */
            Vdbe v; /* The virtual machine being built up */
            int i; /* Loop counter */
            int topOfLoop; /* The top of the loop */
            int endOfLoop; /* The end of the loop */
            int addr; /* The address of an instruction */
            int iDb; /* Index of database containing pTab */
            int regTabname = iMem++; /* Register containing table name */
            int regIdxname = iMem++; /* Register containing index name */
            int regSampleno = iMem++; /* Register containing next sample number */
            int regCol = iMem++; /* Content of a column analyzed table */
            int regRec = iMem++; /* Register holding completed record */
            int regTemp = iMem++; /* Temporary use register */
            int regRowid = iMem++; /* Rowid for the inserted record */

#if SQLITE_ENABLE_STAT2
      int regTemp2 = iMem++;       /* Temporary use register */
      int regSamplerecno = iMem++; /* Index of next sample to record */
      int regRecno = iMem++;       /* Current sample index */
      int regLast = iMem++;        /* Index of last sample to record */
      int regFirst = iMem++;       /* Index of first sample to record */
#endif

            v = SelectHelper.GetVdbe(pParse);
            if (v == null || UnitTest.NEVER(pTab == null) || pTab.pIndex == null)
            {
                /* Do no analysis for tables that have no indices */
                return;
            }
            Debug.Assert(sqlite3BtreeHoldsAllMutexes(db));
            iDb = sqlite3SchemaToIndex(db, pTab.pSchema);
            Debug.Assert(iDb >= 0);
#if !SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, ActionCode.SQLITE_ANALYZE, pTab.zName, 0,
db.aDb[iDb].zName ) ){
return;
}
#endif

            /* Establish a read-lock on the table at the shared-cache level. */
            sqlite3TableLock(pParse, iDb, pTab.tnum, 0, pTab.zName);

            iIdxCur = pParse.nTab++;
            for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
            {
                int nCol = pIdx.nColumn;
                KeyInfo pKey = Build.IndexKeyinfo(pParse, pIdx);

                if (iMem + 1 + (nCol*2) > pParse.nMem)
                {
                    pParse.nMem = iMem + 1 + (nCol*2);
                }

                /* Open a cursor to the index to be analyzed. */
                Debug.Assert(iDb == sqlite3SchemaToIndex(db, pIdx.pSchema));
                VdbeAux.VdbeAddOp4(v, OPCode.OP_OpenRead, iIdxCur, pIdx.tnum, iDb,
                                  pKey, P4Type.P4_KEYINFO_HANDOFF);
                VdbeComment(v, "%s", pIdx.zName);

                /* Populate the registers containing the table and index names. */
                if (pTab.pIndex == pIdx)
                {
                    VdbeAux.VdbeAddOp4(v, OPCode.OP_String8, 0, regTabname, 0, pTab.zName, 0);
                }
                VdbeAux.VdbeAddOp4(v, OPCode.OP_String8, 0, regIdxname, 0, pIdx.zName, 0);

#if SQLITE_ENABLE_STAT2

    /* If this iteration of the loop is generating code to analyze the
** first index in the pTab.pIndex list, then register regLast has
** not been populated. In this case populate it now.  */
        if ( pTab.pIndex == pIdx )
        {
          VdbeAux.VdbeAddOp2( v, OPCode.OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno );
          VdbeAux.VdbeAddOp2( v, OPCode.OP_Integer, SQLITE_INDEX_SAMPLES * 2 - 1, regTemp );
          VdbeAux.VdbeAddOp2( v, OPCode.OP_Integer, SQLITE_INDEX_SAMPLES * 2, regTemp2 );

          VdbeAux.VdbeAddOp2( v, OPCode.OP_Count, iIdxCur, regLast );
          VdbeAux.VdbeAddOp2( v, OPCode.OP_Null, 0, regFirst );
          addr = VdbeAux.VdbeAddOp3( v, OPCode.OP_Lt, regSamplerecno, 0, regLast );
          VdbeAux.VdbeAddOp3( v, OPCode.OP_Divide, regTemp2, regLast, regFirst );
          VdbeAux.VdbeAddOp3( v, OPCode.OP_Multiply, regLast, regTemp, regLast );
          VdbeAux.VdbeAddOp2( v, OPCode.OP_AddImm, regLast, SQLITE_INDEX_SAMPLES * 2 - 2 );
          VdbeAux.VdbeAddOp3( v, OPCode.OP_Divide, regTemp2, regLast, regLast );
          VdbeAux.VdbeJumpHere( v, addr );
        }

        /* Zero the regSampleno and regRecno registers. */
        VdbeAux.VdbeAddOp2( v, OPCode.OP_Integer, 0, regSampleno );
        VdbeAux.VdbeAddOp2( v, OPCode.OP_Integer, 0, regRecno );
        VdbeAux.VdbeAddOp2( v, OPCode.OP_Copy, regFirst, regSamplerecno );
#endif

                /* The block of memory cells initialized here is used as follows.
**
**    iMem:                
**        The total number of rows in the table.
**
**    iMem+1 .. iMem+nCol: 
**        Number of distinct entries in index considering the 
**        left-most N columns only, where N is between 1 and nCol, 
**        inclusive.
**
**    iMem+nCol+1 .. Mem+2*nCol:  
**        Previous value of indexed columns, from left to right.
**
** Cells iMem through iMem+nCol are initialized to 0. The others are 
** initialized to contain an SQL NULL.
*/
                for (i = 0; i <= nCol; i++)
                {
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, 0, iMem + i);
                }
                for (i = 0; i < nCol; i++)
                {
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_Null, 0, iMem + nCol + i + 1);
                }

                /* Start the analysis loop. This loop runs through all the entries in
        ** the index b-tree.  */
                endOfLoop = VdbeAux.VdbeMakeLabel(v);
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Rewind, iIdxCur, endOfLoop);
                topOfLoop = VdbeAux.VdbeCurrentAddr(v);
                VdbeAux.VdbeAddOp2(v, OPCode.OP_AddImm, iMem, 1);

                for (i = 0; i < nCol; i++)
                {
                    VdbeAux.VdbeAddOp3(v, OPCode.OP_Column, iIdxCur, i, regCol);
#if SQLITE_ENABLE_STAT2
          if ( i == 0 )
          {
            /* Check if the record that cursor iIdxCur points to contains a
            ** value that should be stored in the sqlite_stat2 table. If so,
            ** store it.  */
            int ne = VdbeAux.VdbeAddOp3( v, OPCode.OP_Ne, regRecno, 0, regSamplerecno );
            Debug.Assert( regTabname + 1 == regIdxname
            && regTabname + 2 == regSampleno
            && regTabname + 3 == regCol
            );
            VdbeAux.VdbeChangeP5( v, Const.SQLITE_JUMPIFNULL );
            VdbeAux.VdbeAddOp4( v, OPCode.OP_MakeRecord, regTabname, 4, regRec, "aaab", 0 );
            VdbeAux.VdbeAddOp2( v, OPCode.OP_NewRowid, iStatCur + 1, regRowid );
            VdbeAux.VdbeAddOp3( v, OPCode.OP_Insert, iStatCur + 1, regRec, regRowid );

            /* Calculate new values for regSamplerecno and regSampleno.
            **
            **   sampleno = sampleno + 1
            **   samplerecno = samplerecno+(remaining records)/(remaining samples)
            */
            VdbeAux.VdbeAddOp2( v, OPCode.OP_AddImm, regSampleno, 1 );
            VdbeAux.VdbeAddOp3( v, OPCode.OP_Subtract, regRecno, regLast, regTemp );
            VdbeAux.VdbeAddOp2( v, OPCode.OP_AddImm, regTemp, -1 );
            VdbeAux.VdbeAddOp2( v, OPCode.OP_Integer, SQLITE_INDEX_SAMPLES, regTemp2 );
            VdbeAux.VdbeAddOp3( v, OPCode.OP_Subtract, regSampleno, regTemp2, regTemp2 );
            VdbeAux.VdbeAddOp3( v, OPCode.OP_Divide, regTemp2, regTemp, regTemp );
            VdbeAux.VdbeAddOp3( v, OPCode.OP_Add, regSamplerecno, regTemp, regSamplerecno );

            VdbeAux.VdbeJumpHere( v, ne );
            VdbeAux.VdbeAddOp2( v, OPCode.OP_AddImm, regRecno, 1 );
          }
#endif

                    VdbeAux.VdbeAddOp3(v, OPCode.OP_Ne, regCol, 0, iMem + nCol + i + 1);
                    /**** TODO:  add collating sequence *****/
                    VdbeAux.VdbeChangeP5(v, Const.SQLITE_JUMPIFNULL);
                }
                //if( db.mallocFailed ){
                //  /* If a malloc failure has occurred, then the result of the expression 
                //  ** passed as the second argument to the call to VdbeAux.VdbeJumpHere() 
                //  ** below may be negative. Which causes an Debug.Assert() to fail (or an
                //  ** out-of-bounds write if SQLITE_DEBUG is not defined).  */
                //  return;
                //}
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Goto, 0, endOfLoop);
                for (i = 0; i < nCol; i++)
                {
                    VdbeAux.VdbeJumpHere(v, VdbeAux.VdbeCurrentAddr(v) - (nCol*2));
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_AddImm, iMem + i + 1, 1);
                    VdbeAux.VdbeAddOp3(v, OPCode.OP_Column, iIdxCur, i, iMem + nCol + i + 1);
                }

                /* End of the analysis loop. */
                VdbeAux.VdbeResolveLabel(v, endOfLoop);
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Next, iIdxCur, topOfLoop);
                VdbeAux.VdbeAddOp1(v, OPCode.OP_Close, iIdxCur);

                /* Store the results in sqlite_stat1.
        **
        ** The result is a single row of the sqlite_stat1 table.  The first
        ** two columns are the names of the table and index.  The third column
        ** is a string composed of a list of integer statistics about the
        ** index.  The first integer in the list is the total number of entries
        ** in the index.  There is one additional integer in the list for each
        ** column of the table.  This additional integer is a guess of how many
        ** rows of the table the index will select.  If D is the count of distinct
        ** values and K is the total number of rows, then the integer is computed
        ** as:
        **
        **        I = (K+D-1)/D
        **
        ** If K==0 then no entry is made into the sqlite_stat1 table.  
        ** If K>0 then it is always the case the D>0 so division by zero
        ** is never possible.
        */
                addr = VdbeAux.VdbeAddOp1(v, OPCode.OP_IfNot, iMem);
                VdbeAux.VdbeAddOp2(v, OPCode.OP_SCopy, iMem, regSampleno);
                for (i = 0; i < nCol; i++)
                {
                    VdbeAux.VdbeAddOp4(v, OPCode.OP_String8, 0, regTemp, 0, " ", 0);
                    VdbeAux.VdbeAddOp3(v, OPCode.OP_Concat, regTemp, regSampleno, regSampleno);
                    VdbeAux.VdbeAddOp3(v, OPCode.OP_Add, iMem, iMem + i + 1, regTemp);
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_AddImm, regTemp, -1);
                    VdbeAux.VdbeAddOp3(v, OPCode.OP_Divide, iMem + i + 1, regTemp, regTemp);
                    VdbeAux.VdbeAddOp1(v, OPCode.OP_ToInt, regTemp);
                    VdbeAux.VdbeAddOp3(v, OPCode.OP_Concat, regTemp, regSampleno, regSampleno);
                }
                VdbeAux.VdbeAddOp4(v, OPCode.OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
                VdbeAux.VdbeAddOp2(v, OPCode.OP_NewRowid, iStatCur, regRowid);
                VdbeAux.VdbeAddOp3(v, OPCode.OP_Insert, iStatCur, regRec, regRowid);
                VdbeAux.VdbeChangeP5(v, P5Value.OPFLAG_APPEND);
                VdbeAux.VdbeJumpHere(v, addr);
            }
        }

        /*
    ** Generate code that will cause the most recent index analysis to
    ** be laoded into internal hash tables where is can be used.
    */

        private static void loadAnalysis(Parse pParse, int iDb)
        {
            Vdbe v = SelectHelper.GetVdbe(pParse);
            if (v != null)
            {
                VdbeAux.VdbeAddOp1(v, OPCode.OP_LoadAnalysis, iDb);
            }
        }

        /*
    ** Generate code that will do an analysis of an entire database
    */

        private static void analyzeDatabase(Parse pParse, int iDb)
        {
            sqlite3 db = pParse.db;
            Schema pSchema = db.aDb[iDb].pSchema; /* Schema of database iDb */
            HashElem k;
            int iStatCur;
            int iMem;

            Build.BeginWriteOperation(pParse, 0, iDb);
            iStatCur = pParse.nTab;
            pParse.nTab += 2;
            openStatTable(pParse, iDb, iStatCur, null);
            iMem = pParse.nMem + 1;
            //for(k=HashHelper.HashFirst(pSchema.tblHash); k; k=HashHelper.HashNext(k)){
            for (k = pSchema.tblHash.first; k != null; k = k.next)
            {
                var pTab = (Table) k.data; // HashHelper.HashData( k );
                analyzeOneTable(pParse, pTab, iStatCur, iMem);
            }
            loadAnalysis(pParse, iDb);
        }

        /*
    ** Generate code that will do an analysis of a single table in
    ** a database.
    */

        private static void analyzeTable(Parse pParse, Table pTab)
        {
            int iDb;
            int iStatCur;

            Debug.Assert(pTab != null);
            Debug.Assert(sqlite3BtreeHoldsAllMutexes(pParse.db));
            iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema);
            Build.BeginWriteOperation(pParse, 0, iDb);
            iStatCur = pParse.nTab;
            pParse.nTab += 2;
            openStatTable(pParse, iDb, iStatCur, pTab.zName);
            analyzeOneTable(pParse, pTab, iStatCur, pParse.nMem + 1);
            loadAnalysis(pParse, iDb);
        }

        /*
    ** Generate code for the ANALYZE command.  The parser calls this routine
    ** when it recognizes an ANALYZE command.
    **
    **        ANALYZE                            -- 1
    **        ANALYZE  <database>                -- 2
    **        ANALYZE  ?<database>.?<tablename>  -- 3
    **
    ** Form 1 causes all indices in all attached databases to be analyzed.
    ** Form 2 analyzes all indices the single database named.
    ** Form 3 analyzes all indices associated with the named table.
    */
        // OVERLOADS, so I don't need to rewrite parse.c
        public static void sqlite3Analyze(Parse pParse, int null_2, int null_3)
        {
            sqlite3Analyze(pParse, null, null);
        }

        public static void sqlite3Analyze(Parse pParse, Token pName1, Token pName2)
        {
            sqlite3 db = pParse.db;
            int iDb;
            int i;
            string z, zDb;
            Table pTab;
            Token pTableName = null;

            /* Read the database schema. If an error occurs, leave an error message
      ** and code in pParse and return NULL. */
            Debug.Assert(sqlite3BtreeHoldsAllMutexes(pParse.db));
            if (StatusCode.SQLITE_OK != Prepare.ReadSchema(pParse))
            {
                return;
            }

            Debug.Assert(pName2 != null || pName1 == null);
            if (pName1 == null)
            {
                /* Form 1:  Analyze everything */
                for (i = 0; i < db.nDb; i++)
                {
                    if (i == 1) continue; /* Do not analyze the TEMP database */
                    analyzeDatabase(pParse, i);
                }
            }
            else if (pName2.n == 0)
            {
                /* Form 2:  Analyze the database or table named */
                iDb = Build.FindDb(db, pName1);
                if (iDb >= 0)
                {
                    analyzeDatabase(pParse, iDb);
                }
                else
                {
                    z = Build.NameFromToken(db, pName1);
                    if (z != null)
                    {
                        pTab = Build.LocateTable(pParse, 0, z, null);
                        MemPool.DbFree(db, ref z);
                        if (pTab != null)
                        {
                            analyzeTable(pParse, pTab);
                        }
                    }
                }
            }
            else
            {
                /* Form 3: Analyze the fully qualified table name */
                iDb = Build.TwoPartName(pParse, pName1, pName2, ref pTableName);
                if (iDb >= 0)
                {
                    zDb = db.aDb[iDb].zName;
                    z = Build.NameFromToken(db, pTableName);
                    if (z != null)
                    {
                        pTab = Build.LocateTable(pParse, 0, z, zDb);
                        MemPool.DbFree(db, ref z);
                        if (pTab != null)
                        {
                            analyzeTable(pParse, pTab);
                        }
                    }
                }
            }
        }

        /*
    ** Used to pass information from the analyzer reader through to the
    ** callback routine.
    */
        //typedef struct analysisInfo analysisInfo;
        public struct analysisInfo
        {
            public sqlite3 db;
            public string zDatabase;
        };

        /*
    ** This callback is invoked once for each index when reading the
    ** sqlite_stat1 table.
    **
    **     argv[0] = name of the index
    **     argv[1] = results of analysis - on integer for each column
    */

        private static int analysisLoader(object pData, long argc, object Oargv, object NotUsed)
        {
            var argv = (string[]) Oargv;
            var pInfo = (analysisInfo) pData;
            Index pIndex;
            int i, c;
            int v;
            string z;

            Debug.Assert(argc == 2);
            Helper.UNUSED_PARAMETER2(NotUsed, argc);
            if (argv == null || argv[0] == null || argv[1] == null)
            {
                return 0;
            }
            pIndex = Build.FindIndex(pInfo.db, argv[0], pInfo.zDatabase);
            if (pIndex == null)
            {
                return 0;
            }
            z = argv[1];
            int zIndex = 0;
            for (i = 0; z != null && i <= pIndex.nColumn; i++)
            {
                v = 0;
                while (zIndex < z.Length && (c = z[zIndex]) >= '0' && c <= '9')
                {
                    v = v*10 + c - '0';
                    zIndex++;
                }
                pIndex.aiRowEst[i] = v;
                if (zIndex < z.Length && z[zIndex] == ' ') zIndex++;
            }
            return 0;
        }

        /*
    ** If the Index.aSample variable is not NULL, delete the aSample[] array
    ** and its contents.
    */

        private static void sqlite3DeleteIndexSamples(Index pIdx)
        {
#if SQLITE_ENABLE_STAT2
      if ( pIdx.aSample != null )
      {
        int j;
        sqlite3 dbMem = pIdx.pTable.dbMem;
        for ( j = 0; j < SQLITE_INDEX_SAMPLES; j++ )
        {
          IndexSample p = pIdx.aSample[j];
          if ( p.eType == FundamentalDataType.SQLITE_TEXT || p.eType == FundamentalDataType.SQLITE_BLOB )
          {
            p.u.z = null;MemPool.DbFree(pIdx.pTable.dbMem, p.u.z);
            p.u.zBLOB = null;
          }
        }
        MemPool.DbFree(dbMem, pIdx.aSample);
        pIdx.aSample = null;
      }
#else
            Helper.UNUSED_PARAMETER(pIdx);
#endif
        }

        /*
    ** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
    ** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
    ** arrays. The contents of sqlite_stat2 are used to populate the
    ** Index.aSample[] arrays.
    **
    ** If the sqlite_stat1 table is not present in the database, StatusCode.SQLITE_ERROR
    ** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined 
    ** during compilation and the sqlite_stat2 table is present, no data is 
    ** read from it.
    **
    ** If SQLITE_ENABLE_STAT2 was defined during compilation and the 
    ** sqlite_stat2 table is not present in the database, StatusCode.SQLITE_ERROR is
    ** returned. However, in this case, data is read from the sqlite_stat1
    ** table (if it is present) before returning.
    **
    ** If an OOM error occurs, this function always sets db.mallocFailed.
    ** This means if the caller does not care about other errors, the return
    ** code may be ignored.
    */

        private static int sqlite3AnalysisLoad(sqlite3 db, int iDb)
        {
            analysisInfo sInfo;
            HashElem i;
            string zSql;
            int rc;

            Debug.Assert(iDb >= 0 && iDb < db.nDb);
            Debug.Assert(db.aDb[iDb].pBt != null);
            Debug.Assert(sqlite3BtreeHoldsMutex(db.aDb[iDb].pBt));
            /* Clear any prior statistics */
            //for(i=HashHelper.HashFirst(&db.aDb[iDb].pSchema.idxHash);i;i=HashHelper.HashNext(i)){
            for (i = db.aDb[iDb].pSchema.idxHash.first; i != null; i = i.next)
            {
                var pIdx = (Index) i.data; // HashHelper.HashData( i );
                Build.DefaultRowEst(pIdx);
                sqlite3DeleteIndexSamples(pIdx);
            }

            /* Check to make sure the sqlite_stat1 table exists */
            sInfo.db = db;
            sInfo.zDatabase = db.aDb[iDb].zName;
            if (Build.FindTable(db, "sqlite_stat1", sInfo.zDatabase) == null)
            {
                return StatusCode.SQLITE_ERROR;
            }


            /* Load new statistics out of the sqlite_stat1 table */
            zSql = Print.MPrintf(db,
                                  "SELECT idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
            if (zSql == null)
            {
                rc = StatusCode.SQLITE_NOMEM;
            }
            else
            {
                rc = sqlite3_exec(db, zSql, analysisLoader, sInfo, 0);
                MemPool.DbFree(db, ref zSql);
            }


            /* Load the statistics from the sqlite_stat2 table. */
#if SQLITE_ENABLE_STAT2
      if ( rc == StatusCode.SQLITE_OK && null == Build.FindTable( db, "sqlite_stat2", sInfo.zDatabase ) )
      {
        rc = StatusCode.SQLITE_ERROR;
      }
      if ( rc == StatusCode.SQLITE_OK )
      {
        sqlite3_stmt pStmt = null;

        zSql = Print.MPrintf( db,
        "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase );
        //if( !zSql ){
        //rc = StatusCode.SQLITE_NOMEM;
        //}else{
        string sDummy = ""; rc = sqlite3_prepare( db, zSql, -1, ref pStmt, ref sDummy );
        MemPool.DbFree(db, ref zSql);
        //}

        if ( rc == StatusCode.SQLITE_OK )
        {
          while ( sqlite3_step( pStmt ) == StatusCode.SQLITE_ROW )
          {
            string zIndex = sqlite3_column_text( pStmt, 0 );
            Index pIdx = Build.FindIndex( db, zIndex, sInfo.zDatabase );
            if ( pIdx != null )
            {
              int iSample = sqlite3_column_int( pStmt, 1 );
              sqlite3 dbMem = pIdx.pTable.dbMem;
              Debug.Assert( dbMem == db || dbMem == null );
              if ( iSample < SQLITE_INDEX_SAMPLES && iSample >= 0 )
              {
                int eType = sqlite3_column_type( pStmt, 2 );

                if ( pIdx.aSample == null )
                {
                  //static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES;
                  pIdx.aSample = new IndexSample[SQLITE_INDEX_SAMPLES];//(IndexSample *)Malloc.DbMallocZero(dbMem, sz);
                  //if( pIdx.aSample==0 ){
                  //db.mallocFailed = 1;
                  //break;
                  //}
                }

                Debug.Assert( pIdx.aSample != null );
                IndexSample pSample = pIdx.aSample[iSample];
                {
                  pSample.eType = (byte)eType;
                  if ( eType == FundamentalDataType.SQLITE_INTEGER || eType == SQLITE_FLOAT )
                  {
                    pSample.u.r = sqlite3_column_double( pStmt, 2 );
                  }
                  else if ( eType == FundamentalDataType.SQLITE_TEXT || eType == FundamentalDataType.SQLITE_BLOB )
                  {
                    string z = null;
                    byte[] zBLOB = null;
                    //string z = (const char *)(
                    //(eType==FundamentalDataType.SQLITE_BLOB) ?
                    //sqlite3_column_blob(pStmt, 2):
                    //sqlite3_column_text(pStmt, 2)
                    //);
                    if ( eType == FundamentalDataType.SQLITE_BLOB ) zBLOB = sqlite3_column_blob( pStmt, 2 );
                    else z = sqlite3_column_text( pStmt, 2 );
                    int n = sqlite3_column_bytes( pStmt, 2 );
                    if ( n > 24 )
                    {
                      n = 24;
                    }
                    pSample.nByte = (byte)n;
                    pSample.u.z = z;
                    pSample.u.zBLOB = zBLOB;
                    //pSample.u.z = sqlite3DbMallocRaw(dbMem, n);
                    //if( pSample.u.z ){
                    //memcpy(pSample.u.z, z, n);
                    //}else{
                    //db.mallocFailed = 1;
                    //break;
                    //}
                  }
                }
              }
            }
          }
          rc = sqlite3_finalize( ref pStmt );
        }
      }
#endif

            //if( rc==StatusCode.SQLITE_NOMEM ){
            //  db.mallocFailed = 1;
            //}
            return rc;
        }

#endif
        // * SQLITE_OMIT_ANALYZE */
    }
}